const _noBlock = {cancel: !1};
const playPage = chrome.runtime.getURL('play.html');
const r1 = (regp, s) => regp.test(s) && RegExp.$1;
const tabIdList = new Set([-1]);
const reHost = /^https?:\/\/([^/:]+)/;

chrome.runtime.onInstalled.addListener(info => {
	chrome.storage.sync.get({
		domainWhiteList: !1
	}, function(items) {
		if (!items.domainWhiteList) {
			fetch(chrome.runtime.getURL('config.json'))
			.then(r => r.json())
			.then(data => {
				chrome.storage.sync.set(data);
			});
		}
	});
});

let domainWhiteList, jxUrls, useJX;
chrome.storage.sync.get(null, function(items) {
	({domainWhiteList,jxUrls,useJX} = items);
});
const getJXUrl = () => {
	if (useJX < 0) return;
	if (useJX != 999) return jxUrls[useJX].url;
	const i = Math.random() * (jxUrls.length - 1);
	return jxUrls[~~i].url;
};
const checkTab = (id, url) => {
	if (id == -1) return;
	const host = r1(reHost, url);
	if (!host) return;
	tabIdList.delete(id);
	libManager.initOnTab(id);
	if (!domainWhiteList.some(k => host.includes(k))) return true;
	tabIdList.add(id);
};

chrome.tabs.onRemoved.addListener((id, removeInfo) => {
	if (id != -1) {
		libManager.initOnTab(id);
		tabIdList.delete(id);
		allMVs.delete(id);
	}
});
chrome.webNavigation.onCommitted.addListener(details => {
	if (details.frameId > 0) return;
	allMVs.delete(details.tabId);
	if (!enabled || !checkTab(details.tabId, details.url)) return;
	// chrome.tabs.executeScript(details.tabId,{allFrames:true,file:'hookSource.js',runAt:'document_start'});
	chrome.tabs.executeScript(details.tabId,{file:'utils.js',runAt:'document_start'});
	chrome.tabs.executeScript(details.tabId,{file:'m3u8.js',runAt:'document_start'});
});
// 注入代码到子框架
const inject2Frame = (url,tabId,frameId,vType='') => {
	if (!enabled) return;
	const code = `top.postMessage({
		id: 'metal-h5-get-frame',
		url: '${url}',
		vType: '${vType}'
	}, '*');`;
	try {
		chrome.tabs.executeScript(tabId, {frameId, code});
	} catch (ex) {
		// 服务器重定向时iframe已删除，或tab已关闭
	}
};
/* 专门处理多层子框架嵌套
const getTopFrame = (tabId, details) => {
	chrome.webNavigation.getAllFrames(
		{ tabId },
		function (frames) {
			for (let n = details.frameId;;) {
				let frame = frames.find(k => k.frameId == n);
				if (frame.parentFrameId <= 0) {
					details.frameUrl = frame.url;
					chrome.tabs.sendMessage(tabId, details);
					chrome.browserAction.enable(tabId);
					return;
				}
				n = frame.parentFrameId;
			}
		}
	);
}; */

const ulink = new URL('https://www.meiju.com/');
const vKindList = ['mp4','flv','m3u8','mpd','ogg','webm'];
// const audioKinds = ['mp3','m4a','wma','ra','rm'];Manifest
const filter = details => {
	const {tabId, frameId, url, type} = details;
	if (tabIdList.has(tabId) || libManager.isLoaded(tabId) || !vKindList.includes(getExtName(url))) return _noBlock;
	if (frameId != 0) {
		// url = JSON.parse(ulink.searchParams.get('data')).url;
		inject2Frame(url, tabId, frameId);
		libManager.inject(tabId, url);
	}
	else if ('object' == type) {
		chrome.tabs.sendMessage(tabId, {id:'mv-block',url,vType:''});
		libManager.inject(tabId, url);
	}
	else saveMV(tabId, {url});
	return _noBlock;
};

chrome.runtime.onMessage.addListener((msg, sender, cb) => {
	switch (msg.id) {
	case 'native-play':
		chrome.tabs.create({url:`${playPage}?vType=${msg.vType}#${msg.url}`});
		break;
	case 'add-domain-whitelist':
		domainWhiteList.push(msg.host);
		chrome.storage.sync.set({domainWhiteList});
		break;
	case 'getState':
		cb(enabled);
		break;
	case 'getMVList':
		cb(allMVs.get(msg.tabId));
		break;
	case 'getMVCount':
		cb(allMVs.get(msg.tabId)?.length);
		break;
	case 'power':
		enabled = !enabled;
		break;
	case 'inject-lib':
		if (!tabIdList.has(sender.tab.id)) libManager.inject(sender.tab.id, msg.url, msg.vType);
	}
});

// sub_frame
chrome.webRequest.onBeforeRequest.addListener(details => {
		const {tabId, parentFrameId, url} = details;
		if (tabIdList.has(tabId)) return _noBlock;
		ulink.href = url;
		if (ulink.hostname == 'player.youku.com') {
			const jx = getJXUrl();
			if (!jx) return _noBlock;
			const vid = r1(/embed\/([\w=]+)/,ulink.pathname);
			return {redirectUrl: `${jx}https://v.youku.com/v_show/id_${vid}.html`};
		}
		if (ulink.hostname.endsWith('.qq.com')) {
			const jx = getJXUrl();
			if (!jx) return _noBlock;
			const vid = ulink.searchParams.get('firstVid') || ulink.searchParams.get('vid');
			return {redirectUrl: jx +`https://v.qq.com/x/page/${vid}.html`};
		}

		const n = ulink.search.indexOf('=http');
		if (n == -1) return _noBlock;
		let l = ulink.search.indexOf('&', n+21);
		if (l == -1) l = void 0;
		const v = decodeURIComponent(ulink.search.slice(n+1,l));
		if (parentFrameId < 1) {
			const info = { id: 'iframe-block', url: v, frameUrl: url };
			chrome.tabs.sendMessage(tabId, info);
		} else {
			inject2Frame(v, tabId, parentFrameId);
		}
		libManager.inject(tabId, v);
		return {redirectUrl: 'about:blank'};
	},
    { 	urls: [
			'https://player.youku.com/embed/*',
			'https://film.qq.com/*',
			'*://*/*.m3u8*','*://*/*.mp4*'
		],
		types: ['sub_frame'] },
    ['blocking']
);

const baseAddr = chrome.runtime.getURL('lib/');
chrome.webRequest.onBeforeRequest.addListener(details => {
		const {tabId, frameId, url} = details;
		if (!url.startsWith('http')) return _noBlock; //tabIdList.has(tabId) ||
		let {fname} = getFileName(url);
		fname = fname.toLowerCase();
		if (fname.startsWith('flv.')) return {redirectUrl: baseAddr + 'flv.min.js'};
		if (fname.startsWith('dash.')) return {redirectUrl: baseAddr + 'dash.all.min.js'};
		if (fname.startsWith('hls.') || url.endsWith('/Nxplayer/NxHls.min.js'))
			return {redirectUrl: baseAddr + 'hls.min.js'};
		//,'https://www.nxflv.com/Nxplayer/Nxplayer.min.js','https://www.nxflv.com/Nxplayer/NxHls.min.js'
		if (fname.startsWith('dplayer.') || url.endsWith('/Nxplayer/Nxplayer.min.js')) {
			chrome.tabs.insertCSS(tabId,{frameId,file:'player-fix.css'});
			return {redirectUrl: baseAddr + 'DPlayer.min.js'};
		}
	},
    { urls: ['*://*/*.min.js','https://*/js/hls.js'], types: ['script'] },
    ['blocking']
);
chrome.webRequest.onBeforeRequest.addListener(details => {
		const {tabId, url} = details;
		return {cancel: url.startsWith('http')};
	},
    { urls: ['*://*/*/DPlayer.min.css'], types: ['stylesheet'] },
    ['blocking']
);

// object xmlhttprequest
chrome.webRequest.onBeforeRequest.addListener(
	filter, {
		urls: ['*://*/*.flv*','*://*/*.ogg*','*://*/*.mp4*','*://*/*.mpd*','*://*/*.m3u8*','*://*/*.webm*'],
		types: ['object','xmlhttprequest']
	},
    ['blocking']
);

// media
chrome.webRequest.onBeforeSendHeaders.addListener(
	details => {
		const {tabId, frameId, url} = details;
		if (tabIdList.has(tabId)) return _noBlock;
		if (frameId != 0) {
			inject2Frame(url, tabId, frameId, 'normal');
			libManager.inject(tabId, url, 1);
		}
		else if (libManager.isLoaded(tabId)) {
			const i = details.requestHeaders.findIndex(k => k.name.toLowerCase() == 'referer');
			if (i != -1) {
				details.requestHeaders.splice(i, 1);
				return {requestHeaders: details.requestHeaders};
			}
		}
		return _noBlock;
	},
    { urls: ['<all_urls>'], types: ['media']},
    ['blocking','requestHeaders','extraHeaders']
);

// main_frame
chrome.webRequest.onBeforeRequest.addListener(function(info) {
    const url = info.url;
	if (!url.startsWith('http')) return _noBlock;
	/* const playUrl = playPage +'#'+ url;
    if (navigator.userAgent.includes('Firefox')) {
        chrome.tabs.update(info.tabId, {url: playUrl});
        return {cancel: true};
    } */
	if (vKindList.includes(getExtName(url))) return {redirectUrl:playPage +`?vType=auto#${url}`};
}, {
	urls: ['*://*/*.m3u8*','*://*/*.mp4*','*://*/*.ogg*','*://*/*.webm*','*://*/*.mpd*','*://*/*.flv*'],
	types:['main_frame']
}, ['blocking']);

let fnameModel; //v.qq.com 文件名匹配
const getMVSizeFromHead = h => h.find(k => k.name.toLowerCase()=='content-length')?.value;
chrome.webRequest.onHeadersReceived.addListener(details => {// status==200 details.location 重定向地址
		const {tabId, frameId, type, url, responseHeaders: head, initiator: origin} = details;
		if (tabId < 0 || !origin.startsWith('http') ||
			head.find(k => k.name == 'Location')) return;
		let s = head.find(k => k.name.toLowerCase() == 'content-type')?.value.toLowerCase();
		if (!s) return;
		if (type == 'media') {
			if (s.startsWith('text')) return;
			const info = {url, kind: s, size: getMVSizeFromHead(head)};
			saveMV(tabId, info);
			if (s?.startsWith('audio/')) return;
			const item = head.find(k => k.name.toLowerCase() == 'access-control-allow-origin');
			if (item) item.value = '*';
			else head.push({name: 'access-control-allow-origin',value: '*'});
			return {responseHeaders: head};
		}
		const n = s.indexOf(';',-12);
		if (~n) s = s.substr(0, n);
		if (s.startsWith('image') || s.endsWith('stream')) return;
		const info = {url};
		if (s.startsWith('text')) {
			if (getExtName(url) != 'm3u8') return;
			info.kind = 'hls';
		}
		//application/vnd.apple.mpegurl x-mpegURL dash+xml video/x-flv
		else if (s.includes('mpegurl',13) || s.substr(12, 6)=='x-mpeg') info.kind = 'hls';
		else if (s.substr(12, 4) =='dash') info.kind = 'dash';
		else if (s.includes('x-flv',6)) {
			info.kind = 'flv';
			info.size = getMVSizeFromHead(head);
		}
		else if (s.startsWith('video/')) {
			const mhost = origin.substr(-10);
			if (s.substr(6, 4) =='mp2t') {
				if (mhost == '//v.qq.com') {
					if (fnameModel && url.includes(fnameModel, 186) ) return;
					const {pos,fname} = getFileName(url);
					fnameModel = fname.replace(/^\d+_/,'').slice(0,-5); // -5: '.1.ts'
					const u = url.slice(0,pos) + fnameModel +'.ts.m3u8?ver=4';
					saveMV(tabId, {url: u, kind: 'hls'});
				}
				return;
			}
			if (mhost == '.youku.com') return;
			if (mhost == '.douyu.com' && s.substr(6, 5) =='x-p2p') info.kind ='flv';
			else {
				info.size = getMVSizeFromHead(head);
				if (info.size < 5<<20) return; // 5Mb
				info.kind = 'auto';
			}
		}
		// else if (s.startsWith('audio/')) {
			// if (!s.endsWith('/mp4')) saveMV(tabId, {url, kind: s}); // audio/mp4 是dash切片音频数据??
			// return;
		// }
		else return;

		saveMV(tabId, info);
		if (frameId != 0 && !libManager.isLoaded(tabId) && !tabIdList.has(tabId)) {
			inject2Frame(url, tabId, frameId, info.kind);
			libManager.inject(tabId, url, info.kind);
		}
	},
	{urls: ['<all_urls>'], types: ['xmlhttprequest','media']},
	['blocking','responseHeaders',chrome.webRequest.OnBeforeSendHeadersOptions.EXTRA_HEADERS].filter(Boolean)
);